﻿/*
(c) 2010 - Open Source Business Foundation e. V. (www.osbf.de)
This software is provided under Microsoft Reciprocal License (Ms-RL)
(http://www.microsoft.com/opensource/licenses.mspx#Ms-RL)
All other rights reserved
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace MicroFormatProcessing
{
    /// <summary>
    /// This class represents a Microformat out of the database
    /// All properties of the Microformat and all propertynames are stored in a single string each seperated by ,
    /// </summary>
    public class MicroFormat : Microsoft.WindowsAzure.StorageClient.TableServiceEntity
    {

        public string Type { get; set; }
        public string Name { get; set; }

        public string FoundOnUrl { get; set; }
        public DateTime FoundOnDate { get; set; }

        public string PropertyNames { get; set; }
        public string PropertyValues { get; set; }

        public string Tags { get; set; }

        public string References { get; set; }

        private Dictionary<string, string> Properties;
        private HashSet<string> TagSet;
        private HashSet<string> ReferenceSet;

        public MicroFormat() : base()
        {
            Init();
        }

        public MicroFormat(string userId, String rowKey) : base(userId, rowKey)
        {
            Init();
        }

        private void Init()
        {
            this.Properties = new Dictionary<string, string>();
            this.TagSet = new HashSet<string>();
            this.ReferenceSet = new HashSet<string>();
        }

        // copies everything except PartitionKey and RowKey
        public void Init(MicroFormat otherMicroFormat)
        {
            if (otherMicroFormat != null)
            {
                this.Type = otherMicroFormat.Type;
                this.Name = otherMicroFormat.Name;
                this.FoundOnUrl = otherMicroFormat.FoundOnUrl;
                this.FoundOnDate = otherMicroFormat.FoundOnDate;
                this.PropertyNames = otherMicroFormat.PropertyNames;
                this.PropertyValues = otherMicroFormat.PropertyValues;
                this.Tags = otherMicroFormat.Tags;
                this.References = otherMicroFormat.References;
                foreach (string propertyName in otherMicroFormat.GetPropertyNameArray())
                {
                    this.SetProperty(propertyName, otherMicroFormat.GetProperty(propertyName));
                }
                foreach (string tag in otherMicroFormat.GetTagArray())
                {
                    this.AddTag(tag);
                }
                foreach (string reference in otherMicroFormat.GetReferenceArray())
                {
                    this.AddReference(reference);
                }
            }
        }

        // clears everything except PartitionKey and RowKey

        public void ClearPoperties() {
            this.Properties.Clear();
            this.PropertyNames = "";
            this.PropertyValues = "";
        }

        public void ClearTags() {
            this.Tags = "";
            this.TagSet.Clear();
        }

        public void Clear()
        {
            this.Type = "";
            this.Name = "";
            this.FoundOnUrl = "";
            this.FoundOnDate = DateTime.MinValue;
            this.References = "";
            this.ReferenceSet.Clear();
            ClearTags();
            ClearPoperties();
        }

        public string GetProperty(string propertyName)
        {
            if (this.Properties.ContainsKey(propertyName)) {
                return this.Properties[propertyName];
            } else {
                return "";
            }
        }

        public void SetProperty(string propertyName, string propertyValue)
        {
            if ((propertyName != null) && (propertyName.Length > 0))
            {
                this.Properties[propertyName] = propertyValue;
            }
        }

        public string[] GetPropertyNameArray()
        {
            return this.Properties.Keys.ToArray<string>();
        }

        public bool HasTag(string tag)
        {
            return this.Tags.Contains(tag);
        }

        public int GetTagCount()
        {
            return this.TagSet.Count;
        }

        public string[] GetTagArray()
        {
            return this.TagSet.ToArray<string>();
        }

        public bool AddTag(string tag)
        {
            if ((tag != null) && (tag.Length > 0))
            {
                return this.TagSet.Add(tag);
            }
            else
            {
                return false;
            }
        }

        public bool RemoveTag(string tag)
        {
            if ((tag != null) && (tag.Length > 0))
            {
                return this.TagSet.Remove(tag);
            }
            else
            {
                return false;
            }
        }

        public int GetReferenceCount()
        {
            return this.ReferenceSet.Count;
        }

        public string[] GetReferenceArray()
        {
            return this.ReferenceSet.ToArray<string>();
        }

        public bool AddReference(string reference)
        {
            if ((reference != null) && (reference.Length > 0))
            {
                return this.ReferenceSet.Add(reference);
            }
            else
            {
                return false;
            }
        }

        public string GetUserId()
        {
            return this.PartitionKey;
        }

        public void SetUserId(string userId)
        {
            this.PartitionKey = userId;
        }

        public string GetPropertyHash()
        {
            return this.RowKey;
        }

        public void SetPropertyHash(string propertyHash)
        {
            this.RowKey = propertyHash;
        }

        public void SerializeAttributes()
        {
            string[] propertyNameArray = this.GetPropertyNameArray();
            Array.Sort(propertyNameArray);
            this.PropertyNames = SerializeUtil.SerializeArray(propertyNameArray);
            string[] propertyValueArray = new string[propertyNameArray.Length];
            for (int i = 0; i < propertyNameArray.Length; i++)
            {
                propertyValueArray[i] = this.GetProperty(propertyNameArray[i]);
            }
            this.PropertyValues = SerializeUtil.SerializeArray(propertyValueArray);
            if ((this.GetPropertyHash() == null) || (this.GetPropertyHash().Length == 0))
            {
                this.CalculateNewPropertyHash();
            }
            this.References = SerializeUtil.SerializeArray(this.GetReferenceArray());
            this.Tags = SerializeUtil.SerializeArray(this.GetTagArray());
        }

        public void DeSerializeAttributes()
        {
            string[] propertyNameArray = SerializeUtil.DeSerializeString(this.PropertyNames);
            string[] propertyValueArray = SerializeUtil.DeSerializeString(this.PropertyValues);
            for (int i = 0; i < propertyNameArray.Length; i++)
            {
                this.SetProperty(propertyNameArray[i], propertyValueArray[i]);
            }
            string[] referenceArray = SerializeUtil.DeSerializeString(this.References);
            for (int i = 0; i < referenceArray.Length; i++)
            {
                this.AddReference(referenceArray[i]);
            }
            string[] tagArray = SerializeUtil.DeSerializeString(this.Tags);
            for (int i = 0; i < tagArray.Length; i++)
            {
                this.AddTag(tagArray[i]);
            }
        }

        private void CalculateNewPropertyHash()
        {
            StringBuilder hashBuilder = new StringBuilder();
            hashBuilder.Append(this.PropertyNames).Append(SerializeUtil.DELIMITER).Append(this.PropertyValues);
            byte[] hashSource = System.Text.Encoding.UTF8.GetBytes(hashBuilder.ToString());
            byte[] hashBytes = new MD5CryptoServiceProvider().ComputeHash(hashSource);
            StringBuilder hashString = new StringBuilder(hashBytes.Length);
            for (int i = 0; i < hashBytes.Length; i++)
            {
                hashString.Append(hashBytes[i].ToString("X2"));
            }
            this.SetPropertyHash(hashString.ToString());
        }

        public string ToHtml()
        {
            StringBuilder html = new StringBuilder();
            string typeClass = this.Type;
            if ("hCard".Equals(typeClass))
            {
                typeClass = "vcard";
            }
            if ("hCalendar".Equals(typeClass))
            {
                typeClass = "vevent";
            }
            html.Append("<span class=\"").Append(typeClass).Append("\">");
            foreach (string propertyName in this.GetPropertyNameArray())
            {
                if ("dtstart".Equals(propertyName) || "dtend".Equals(propertyName))
                {
                    html.Append("<abbr class=\"").Append(propertyName).Append("\"")
                        .Append(" title=\"").Append(this.GetProperty(propertyName)).Append("\"")
                        .Append(" />");
                }
                else if ("url".Equals(propertyName))
                {
                    html.Append("<a class=\"").Append(propertyName).Append("\"")
                        .Append(" href=\"").Append(this.GetProperty(propertyName)).Append("\"")
                        .Append(">").Append(this.GetProperty(propertyName)).Append("</a>");
                }
                else if ("email".Equals(propertyName))
                {
                    html.Append("<a class=\"").Append(propertyName).Append("\"")
                        .Append(" href=\"mailto:").Append(this.GetProperty(propertyName)).Append("\"")
                        .Append(">").Append(this.GetProperty(propertyName)).Append("</a>");
                }
                else
                {
                    html.Append("<span class=\"").Append(propertyName).Append("\">")
                        .Append(this.GetProperty(propertyName))
                        .Append("</span>");
                }
            }
            html.Append("</span>");
            return html.ToString();
        }
    }
}